package uk.co.q3c.deplan.server;

import java.util.EnumMap;

import org.apache.log4j.Logger;

import uk.co.q3c.deplan.client.domain.DomainKind;
import uk.co.q3c.deplan.client.domain.DomainObject;
import uk.co.q3c.deplan.client.domain.dag.DAG;
import uk.co.q3c.deplan.client.domain.dag.Hierarchy;
import uk.co.q3c.deplan.client.domain.resource.BaseCalendar;
import uk.co.q3c.deplan.client.domain.resource.IndividualResource;
import uk.co.q3c.deplan.client.domain.resource.ResourceCalendar;
import uk.co.q3c.deplan.client.domain.resource.ResourceDailyProfile_LinkedList;
import uk.co.q3c.deplan.client.domain.resource.ResourceGroup;
import uk.co.q3c.deplan.client.domain.resource.ResourcePool;
import uk.co.q3c.deplan.client.domain.resource.ResourceProfileEntry;
import uk.co.q3c.deplan.client.domain.task.BaseTask;
import uk.co.q3c.deplan.client.domain.task.PlanDailyProfile;
import uk.co.q3c.deplan.client.domain.task.PlanProfileEntry;
import uk.co.q3c.deplan.client.domain.task.Task;
import uk.co.q3c.deplan.client.domain.util.CalendarAdjustments;
import uk.co.q3c.deplan.client.service.PlanManager;
import uk.co.q3c.deplan.client.service.ServiceFactory;
import uk.co.q3c.deplan.server.admin.ServerServiceFactory;
import uk.co.q3c.deplan.server.dao.AdminDao;
import uk.co.q3c.deplan.server.domain.util.shard.DatastoreCounter;
import uk.co.q3c.deplan.server.domain.util.shard.DatastoreCounterShard;

import com.googlecode.objectify.Objectify;
import com.googlecode.objectify.ObjectifyService;

/**
 * Utility classes for initialising the server side code. Creates and holds an instance of a PersistenceConnection for access to the
 * datastore <br>
 * Manages the counters used for keyIds, as these are global to the database
 * 
 * @author David Sowerby 8 Mar 2010
 * 
 */
public class Server {
	static transient protected final Logger logger = Logger.getLogger(Server.class.getName());
	static public AdminDao adminDao;
	static EnumMap<DomainKind, Class<?>> allDomainClasses;
	public static EnumMap<DomainKind, Class<? extends DomainObject>> taskClasses;
	static EnumMap<DomainKind, Class<? extends DomainObject>> resourceClasses;

	private static ServerServiceFactory connection;
	static {
		allDomainClasses = new EnumMap<DomainKind, Class<?>>(DomainKind.class);
		taskClasses = new EnumMap<DomainKind, Class<? extends DomainObject>>(DomainKind.class);
		resourceClasses = new EnumMap<DomainKind, Class<? extends DomainObject>>(DomainKind.class);

		allDomainClasses.put(DomainKind.IndividualResource, IndividualResource.class);
		allDomainClasses.put(DomainKind.PlanManager, PlanManager.class);
		allDomainClasses.put(DomainKind.BaseCalendar, BaseCalendar.class);
		allDomainClasses.put(DomainKind.ResourceGroup, ResourceGroup.class);
		allDomainClasses.put(DomainKind.AbstractTask, BaseTask.class);
		allDomainClasses.put(DomainKind.ResourceDailyProfile, ResourceDailyProfile_LinkedList.class);
		allDomainClasses.put(DomainKind.ResourceCalendar, ResourceCalendar.class);
		allDomainClasses.put(DomainKind.CalendarAdjustments, CalendarAdjustments.class);
		allDomainClasses.put(DomainKind.ResourceProfileEntry, ResourceProfileEntry.class);
		allDomainClasses.put(DomainKind.PlanProfileEntry, PlanProfileEntry.class);
		allDomainClasses.put(DomainKind.PlanDailyProfile, PlanDailyProfile.class);
		allDomainClasses.put(DomainKind.ResourcePool, ResourcePool.class);

		taskClasses.put(DomainKind.AbstractTask, BaseTask.class);

		resourceClasses.put(DomainKind.IndividualResource, IndividualResource.class);
		resourceClasses.put(DomainKind.ResourceGroup, ResourceGroup.class);

		// --- register the classes for Objectify

		for (Class<?> clazz : allDomainClasses.values()) {
			logger.info(clazz.getSimpleName());
			ObjectifyService.register(clazz);
		}

		ObjectifyService.register(DatastoreCounter.class);
		ObjectifyService.register(DatastoreCounterShard.class);
		ObjectifyService.register(DAG.class);
		ObjectifyService.register(Hierarchy.class);
		// ObjectifyService.register(DAGVertexChange.class);
		// ObjectifyService.register(DAGEdgeChange.class);
		// ObjectifyService.setDatastoreTimeoutRetryCount(3);
	}

	public static ServiceFactory connection() {
		if (connection == null) {
			connection = new ServerServiceFactory();
		}
		return connection;
	}

	/**
	 * Invoke only when creating a new database
	 * 
	 */

	private static void initialiseCounters() {
		connection(); // make sure connection is created first
		connection.adminDao()
				.createCounter("task");
		connection.adminDao()
				.createCounter("resource");
		connection.adminDao()
				.createCounter("other");
		if (logger.isDebugEnabled()) {
			int c = connection.adminDao()
					.countCounters();
			logger.debug(c + " counters initialised");
		}
	}

	public static void createDatabase() {
		initialiseCounters();

	}

	public static void prepareInstance() {
		// These must come first, as they are used as soon as the task service is accessed.
		DAG dependencies = new DAG();
		dependencies.setKeyId((long) 1);
		dependencies.setVersion(1);
		Objectify ofy = ObjectifyService.begin();
		ofy.put(dependencies);

		Hierarchy h = new Hierarchy();
		h.setKeyId((long) 1);
		h.setVersion(1);
		ofy.put(h);

		PlanManager planManager = new PlanManager(connection());
		connection.taskService()
				.save(planManager);
		BaseCalendar baseCalendar = new BaseCalendar(connection);
		connection.calendarService()
				.save(baseCalendar);

		// fix dependencies id at 1 - there should only ever be one of them

	}

	@SuppressWarnings("unchecked")
	public static Class<? extends Task> taskClassForKind(DomainKind domainKind) {
		return (Class<? extends Task>) taskClasses.get(domainKind);
	}

	public static void reset() {
		connection = null;
	}

}
